#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：idi_mes 
@File    ：tool.py
@IDE     ：PyCharm 
@Author  ：孔令伟
@Date    ：9/25/24 9:26 AM 
"""
import json
from collections import Counter
from datetime import datetime, timedelta
from typing import Dict, Tuple, List

import requests
from mg_app_framework import get_logger, MesCode, get_context, update_context

from common.basic_mongo import (get_batch_code_config_collection, get_feeding_config_collection,
                                get_retrospect_collection, get_rule_code_collection,
                                get_new_batch_code_config_collection, get_prod_result_param_collection,
                                get_basic_config, get_unqualified_records_backup, get_unqualified_collection,
                                get_routing_process_info)
from common.constant import RetrospectType
from common.container_operate.container_status import get_product_list_by_container_mark
from common.retrospect_operate.handle import publish_retrospect_nodes, filter_device_bom_config, \
    filter_device_code_workorder_config
from common.retrospect_operate.material_tools import get_unique_id


async def check_repeat_in_or_out(procedure_code: str, qr_code_list: list, op_type: str) -> list:
    """
    重复进站、重复出站校验，具体进站还是出站校验取决于入参的op_type
    :param qr_code_list:
    :param procedure_code:
    :param op_type:
    :return:
    """
    # 如果当前工序追溯信息已经查到该条码存在投料或者产出的信息，则认为该条码现在再次进站或出站属于重复操作
    query_filter = {
        "qr_code": {"$in": qr_code_list},
        "procedure_code": procedure_code,
        "op_type": op_type,
        "is_delete": False,
    }
    collection = get_retrospect_collection()
    qr_code_has_used = []  # 列表中存储所有已经在该工序有进站、或出站记录的条码
    async for document in collection.find(query_filter):
        qr_code = document.get("qr_code")
        qr_code_has_used.append(qr_code)
    return list(set(qr_code_has_used))


def check_basic_param(params: Dict) -> Tuple[bool, str]:
    """
    '接口调用方认证ID/接口调用ID/上位机编码/员工编码/设备编码'是每一个接口都有且必填的入参，检查这些参数是否填写
    :param params:{
               "call_id":"JQ00386",  #接口调用方认证ID, string
               "trace_id":"8941d16b-0581-4e68-8940-a651afff4282",  #接口调用ID, string
               "upperpc_code": "UP011",    #上位机编码, string
               "user_code":"Y001",  #员工编码, string
               "equip_code":"ZP001",   #设备编码, string
               ...
              }
    :return:
    """
    equip_code, process_code = params.get("equip_code"), params.get("process_code")

    if not equip_code or not process_code:
        return False, "请检查入参是否完整填写'设备编码/工序编码';"
    else:
        error_msg = ""
        if error_msg:
            return False, error_msg
        else:
            return True, ""


# async def get_material_code(qr_code: str, op_type: str, procedure_code: str) -> str:
#     """
#     建立追溯关系时，获取物料变码：
#     1. 根据码值查询最新的追溯信息，拿到物料信息；
#     2. 如果1不存在最新追溯，说明变码产生的新码值，则在变码表查询该码值得到物料编码；
#     3. 如果2也不存在，提示码值异常；
#     :param procedure_code:
#     :param op_type:
#     :param qr_code:
#     :return:
#     """
#     query_filter = {
#         "qr_code": qr_code,
#         "procedure_code": procedure_code,
#         "op_type": op_type,
#         "is_delete": False,
#     }
#     collection = get_retrospect_collection()
#     material_code = ""
#     async for document in collection.find(query_filter).sort(["op_time", -1]).limit(1):
#         material_code = document.get("material_code")
#
#     if material_code:
#         return material_code
#     else:
#         # 原料码值与物料的映射关系
#         qr_code_material_code_map: dict = await get_batch_code_material_code_map(
#             code_list=[qr_code]
#         )
#         material_code = qr_code_material_code_map.get(qr_code, "")
#
#         if material_code:
#             return material_code
#         else:
#             return ""


def get_time(now: datetime = None):
    """

    :param now:
    :return:
    """
    if not now:
        now = datetime.now()
    return now.strftime("%Y-%m-%d %H:%M:%S")


async def get_material_code_num_map(code_list: list) -> dict:
    """
    获取系统的物料配置表中各物料的可用数量
    :param code_list:
    :return:
    """
    collection = get_batch_code_config_collection()
    query_filter = {"qr_code": {"$in": code_list}}
    result = {}
    async for rec in collection.find(query_filter):
        available_num = rec["available_num"]
        qr_code = rec["qr_code"]
        material_code = rec["material_code"]
        result.update({
            qr_code: {"material_code": material_code, "available_num": available_num}
        })
    return result


async def get_material_code_num_map_new(code_list: list) -> dict:
    """
    获取系统的物料配置表中各物料的可用数量
    :param code_list:
    :return:
    """
    collection = get_new_batch_code_config_collection()
    query_filter = {"qr_code": {"$in": code_list}}
    result = {}
    async for rec in collection.find(query_filter):
        available_num = rec["available_num"]
        qr_code = rec["qr_code"]
        material_code = rec["material_code"]
        result.update({
            qr_code: {"material_code": material_code, "available_num": available_num}
        })

    return result


async def get_feeding_material_code_num_map(code_list: list, process_code: str) -> dict:
    """
    获取系统当前投料的各批次码对应的物料的投料数量
    :param code_list:
    :param process_code:
    :return:
    """
    collection = get_feeding_config_collection()
    query_filter = {"material_code": {"$in": code_list},
                    "process_code": process_code,
                    "is_feeding": True}
    material_code_qty_map: dict = {}
    async for rec in collection.find(query_filter):
        material_code = rec["material_code"]
        qty = rec["qty"]
        if qty > 0:
            if material_code not in material_code_qty_map:
                material_code_qty_map.update({material_code: qty})
            else:
                material_code_qty_map[material_code] += qty

    return material_code_qty_map


async def calculate_feeding_material_qty(material_code_quota_map: dict, process_code: str, equip_code: str):
    """
    在原材料批次码与投料数量所处的子表中进行扣减操作
    :param equip_code:
    :param process_code:
    :param material_code_quota_map:
    :return:
    """
    """
    扣料逻辑： 在物料关联的最早的一条投料数量大于0投料记录上扣减，即使该记录的投料数量可能能被扣减为负数
    """
    collection = get_feeding_config_collection()
    for material_code, reduction_qty in material_code_quota_map.items():
        # 查询符合条件的记录，按照time倒序排列
        document = await collection.find_one(
            {
                "process_code": process_code,
                "equip_code": equip_code,
                "material_code": material_code,
                "qty": {"$gt": 0},
            }, sort=[("time", 1)]
        )
        if document:
            document_id = document["_id"]
            document_qty = document["qty"]
            new_qty = document_qty - reduction_qty
            await collection.find_one_and_update(
                {"_id": document_id},
                {"$set": {"qty": new_qty}}
            )


async def calculate_feeding_material_qty_036_060(material_code_quota_map: dict, process_code: str, equip_code: str,
                                                 code_list: list):
    """
    在原材料批次码与投料数量所处的子表中进行扣减操作
    :param code_list:
    :param equip_code:
    :param process_code:
    :param material_code_quota_map:
    :return:
    """
    """
    扣料逻辑： 在物料关联的最早的一条投料数量大于0投料记录上扣减，即使该记录的投料数量可能能被扣减为负数
    """
    collection = get_feeding_config_collection()
    material_code_list = list(material_code_quota_map.keys())
    for code in code_list:
        # 查询符合条件的记录，按照time倒序排列
        document = await collection.find_one(
            {
                "lot_no": code,
                "process_code": process_code,
                "equip_code": equip_code,
                "material_code": {"$in": material_code_list},
                "qty": {"$gt": 0},
            }, sort=[("time", 1)]
        )
        if document:
            document_id = document["_id"]
            document_qty = document["qty"]
            material_code = document["material_code"]
            reduction_qty = material_code_quota_map[material_code]
            new_qty = document_qty - reduction_qty
            await collection.find_one_and_update(
                {"_id": document_id},
                {"$set": {"qty": new_qty}}
            )


async def get_input_lot_no_list(material_code_quota_map: dict, process_code: str, equip_code: str) \
        -> list:
    """
    一些工序是投料、出站，没有进站操作，因此需要通过本方法知道该完工出站时扣减的是哪些原材料批次码，
    然后依据这些原材料批次码在出站时构建投料追溯数据
    :param equip_code:
    :param process_code:
    :param material_code_quota_map:
    :return:
    """
    """
    扣料逻辑： 在物料关联的最早的一条投料数量大于0投料记录上扣减，即使该记录的投料数量可能能被扣减为负数
    """
    collection = get_feeding_config_collection()
    input_raw_lot_no_list = []
    for material_code, reduction_qty in material_code_quota_map.items():
        # 查询符合条件的记录，按照time倒序排列
        document = await collection.find_one(
            {
                "process_code": process_code,
                "equip_code": equip_code,
                "material_code": material_code,
                "qty": {"$gt": 0},
            }, sort=[("time", 1)]
        )
        if document:
            document_lot_no = document["lot_no"]
            input_raw_lot_no_list.append(document_lot_no)

    return input_raw_lot_no_list


async def make_retrospect_data_and_publish(param: dict) -> Tuple[bool, str, list]:
    """
    在完工出站的时候建立该物料的进站、出站追溯数据
    :param param: {"procedure_code":"", "device_code":"", "is_ng":"", "qr_code":""}
    :return:
    """
    """
    出站时创建出站的追溯数据，这部分功能修改为调用提供的出站数据追溯数据创建接口；
    提供的出站追溯数据创建接口需要添加通过在制品单件码在bom_info获取批次码，并对批次码进行原材料扣减的操作
    """
    error_msg = ""
    device_code = param.get("device_code")
    qr_code = param.get("qr_code")
    out_material_code = param.get("material_code", None)
    workorder_code = param.get("workorder_code", None)
    # 用于识别原材料扣减的时候是否用指定的扣减数量进行扣减操作，目前仅补液接口（interface_type='fill_electrolyte'）在原材料扣减时要用
    # （补液后重量-补液前重量）的数值（param_material_quota）作为原材料的扣减数值，其它出站接口在调用本方法的时候扣减原材料的数值使用的是BOM配置中该原材料的数值。
    interface_type = param.get("interface_type", None)
    param_material_quota = param.get("material_quota", None)
    ng_list = param.get("ng_list", [])
    input_time = param.get("input_time", None)
    output_time = param.get("output_time", None)
    if not workorder_code:
        workorder_config = await filter_device_code_workorder_config(device_code=device_code)
        work_order = workorder_config.workorder_code
        unique_id = get_unique_id()
        workorder_code = f"{work_order}~{unique_id}"

    # 从bom信息获取本工序投入物料中code-type分类是批次码的物料及其扣减数量
    material_code_material_quota_map = {}
    bom_info = await filter_device_bom_config(device_code=device_code)
    if not bom_info.product_in:
        return False, f"设备{device_code}的工序BOM不存在;", []
    for product_in_bom_info in bom_info.product_in:
        if product_in_bom_info.code_type == "batch_code":
            material_code = product_in_bom_info.material_code
            material_quota = product_in_bom_info.material_quota if interface_type != "fill_electrolyte" else param_material_quota
            if material_code in material_code_material_quota_map:
                material_code_material_quota_map[material_code] += material_quota
            else:
                material_code_material_quota_map.update({material_code: material_quota})
    # 本工序投入的物料批次码，依据投料批次码创建投入追溯数据
    input_lot_no_list = await get_input_lot_no_list(material_code_quota_map=material_code_material_quota_map,
                                                    process_code=param.get("procedure_code"),
                                                    equip_code=param.get("device_code"))
    insert_data_list: list = []
    if interface_type == "output_wip_usesn":
        code_list = param.get('code_list')
        for lot_no in code_list:
            insert_data_list.append({"qr_code": lot_no,
                                     "op_type": RetrospectType.PRODUCT_IN,
                                     "check_result": "",
                                     "ng_list": [],
                                     "work_order_code": workorder_code,
                                     "op_time": input_time})  # 投入追溯数据
    else:
        for lot_no in input_lot_no_list:
            insert_data_list.append({
                "qr_code": lot_no,
                "op_type": RetrospectType.PRODUCT_IN,
                "check_result": "",
                "ng_list": [],
                "work_order_code": workorder_code,
                "op_time": input_time,
                # 补液接口025中，当补液后重量>补液前重量，需要用重量差值作为原材料扣减的数量，不再是从BOM获取原材料扣减数量，
                # 因此追溯数据上记录原材料扣减数量的值也需要更新为重量的差值
                "material_quota": None if interface_type != "fill_electrolyte" else param_material_quota
            })  # 投入追溯数据
    insert_data_list.append({"qr_code": qr_code,
                             "op_type": RetrospectType.PRODUCT_OUT,
                             "check_result": "" if param.get("is_ng") == 0 else "不合格",
                             "ng_list": ng_list,
                             "material_code": out_material_code,
                             "work_order_code": workorder_code,
                             "op_time": output_time})  # 产出追溯数据
    try:
        await publish_retrospect_nodes(device_code=device_code, retrospect_items=insert_data_list)
        get_logger().info(f"码值{qr_code}出站追溯数据已建立")
    except ValueError as e:
        error_msg += str(e)
    if error_msg:
        return False, f"{qr_code}:{error_msg};", []
    # 构建追溯数据后对投料数据进行扣减操作
    if interface_type == "output_wip_usesn":
        code_list = param.get('code_list')
        await calculate_feeding_material_qty_036_060(
            material_code_quota_map=material_code_material_quota_map,
            process_code=param.get("procedure_code"),
            equip_code=param.get("device_code"),
            code_list=code_list
        )
    else:
        await calculate_feeding_material_qty(
            material_code_quota_map=material_code_material_quota_map,
            process_code=param.get("procedure_code"),
            equip_code=param.get("device_code"),
        )
    return True, f"码值{qr_code}出站追溯数据已建立;", input_lot_no_list


async def get_qrcode_latest_retros(wip_no_list: list, op_type: str, process_code: str):
    """
    判断码值最新的进出站操作是不是本工序进站
    :param process_code:
    :param wip_no_list:
    :param op_type:
    :return:
    """
    retors_coll = get_retrospect_collection()
    query_filter = {
        "qr_code": {"$in": wip_no_list},
        "is_delete": False,
    }
    pipeline = [
        {"$match": query_filter},
        {"$sort": {"op_time": -1}},
        {"$group": {
            "_id": "$qr_code",
            "latest_document": {"$first": "$$ROOT"}
        }},
        {"$project": {
            "qr_code": "$_id",
            "op_type": "$latest_document.op_type",
            "process_code": "$latest_document.procedure_code",
            "_id": 0
        }}
    ]

    qr_code_list = []
    async for rec in retors_coll.aggregate(pipeline):
        if rec.get("op_type", "") == op_type and rec.get("process_code") == process_code:
            pass
        else:
            qr_code_list.append(rec.get('qr_code'))
    if not qr_code_list:
        return True, ""
    else:
        return False, f"码值{qr_code_list}最新的进出站操作不是本工序进站;"


async def check_rule_code_exists(wip_no_list: List[str], wip_no_bind_wip_no_map: Dict[str, str]) -> str:
    """
    判断负极电芯码或正极电芯码是否在MES中已经生成
    :param wip_no_bind_wip_no_map:
    :param wip_no_list:
    :return:
    """
    collection = get_rule_code_collection()
    query_filter = {
        "code": {"$in": wip_no_list}
    }
    query_code_list, query_bind_no_no_map_list = [], []
    async for rec in collection.find(query_filter):
        code = rec["code"]
        bind_code = rec["bind_code"]
        query_code_list.append(code)
        if bind_code != wip_no_bind_wip_no_map.get(code, ""):
            query_bind_no_no_map_list.append(code)

    query_code_list: list = await collection.distinct('code', query_filter) or []
    not_found_batch_code: set = set(wip_no_list) - set(query_code_list)

    error_msg = ""
    if not_found_batch_code:
        error_msg += f"下列产出在制品唯一码没有在MES中生成:{list(not_found_batch_code)}"
    if query_bind_no_no_map_list:
        error_msg += f"下列产出在制品唯一码绑定进站码有误:{query_bind_no_no_map_list}"

    return error_msg


async def check_cell_sn_consistent(vehicle_no: str, cell_sn_list: list):
    """
    对比托盘码在数据库中绑定的电芯码是否与入参中的电芯码一致
    :param vehicle_no:
    :param cell_sn_list:
    :return:
    """
    # 获取数据库中该托盘码绑定的电芯码信息
    cell_list_in_db = await get_product_list_by_container_mark(container_mark=vehicle_no)
    cell_sn_list_in_db = [rec['mark'] for rec in cell_list_in_db]
    # 对比托盘码在数据库中绑定的电芯码是否与入参中的电芯码一致
    if Counter(cell_sn_list) != Counter(cell_sn_list_in_db):
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": f"载具码在系统中绑定的电芯码与入参电芯码不一致;"  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
        }
        return False, result, cell_sn_list_in_db
    else:
        return True, {}, cell_sn_list_in_db


async def device_workorder(device_code):
    """

    :param device_code:
    :return:
    """
    workorder_config = await filter_device_code_workorder_config(device_code=device_code)
    work_order = workorder_config.workorder_code
    return work_order


async def device_bom(device_code):
    """

    :param device_code:
    :return:
    """
    workorder_config = await filter_device_code_workorder_config(device_code=device_code)
    bom_info = workorder_config.bom_info
    return bom_info


def batch_make_retros_in(wip_list, work_order):
    """
    生成进站追溯数据基础数据
    :param wip_list:
    :param work_order:
    :return:
    """
    insert_data_list = []
    for index, qr_code in enumerate(wip_list):
        # 通过timedelta来保证批量进站时每一个进站数据的wororder_code都不一样
        now_time_stamp = (datetime.now() + timedelta(microseconds=index)).strftime("%Y%m%d%H%M%S.%f")
        unique_id = now_time_stamp.replace(".", "")
        insert_data_list.append({"qr_code": qr_code,
                                 "check_result": "",
                                 "op_type": RetrospectType.PRODUCT_IN,
                                 "ng_list": [],
                                 "work_order_code": f"{work_order}~{unique_id}"})
    return insert_data_list


async def send_unqualified_http(unqualified_records: list):
    """

    :param unqualified_records:
    :return:
    """
    # 蓝京环境地址
    # url = "http://47.99.135.166:41020/lanjing/api/production_material_mgm/create_config_item"
    # 武汉生产测试环境地址
    # url = "http://223.76.187.243:8888/mes/api/production_material_mgm/create_config_item"
    url = "http://localhost:39358/api/lanjing_unqualified_product_control/external/register_by_http"
    result = await send_http_msg(url, unqualified_records)
    get_logger().info(f"send_unqualified_http:unqualified_records:{unqualified_records};result:{result}")
    return result


async def send_http_msg(url, msg):
    """
    用于发送HTTP请求
    :param url:
    :param msg:
    :return:
    """
    payload = json.dumps(msg)
    headers = {
        'Content-Type': 'application/json'
    }
    # noinspection PyBroadException
    try:
        response = requests.request("POST", url, headers=headers, data=payload)
        if response.status_code == 200:
            response_info = json.loads(response.text)
            code, data, info = response_info['code'], response_info['data'], response_info['info']
            if code == "success":
                return True, data, info
            elif code == "fail":
                return False, data, info
        else:
            info = f"HTTP返回异常:状态码:{response.status_code};响应信息:{json.loads(response.text)}"
            return False, {}, info
    except Exception as e:
        info = f"HTTP请求期间发生错误error：{str(e)}, url:{url}"
        get_logger().info(info)
        return False, {}, info


async def insert_prod_result_param(params: list):
    """
    033、034、035、036、037、038接口的码值与param-list及基础参数组建一条或多条记录，并写入这张表；
    :param params:[{
                "call_id" : "JQ01058",
                "trace_id" : "8941d16b-0581-4e68-8940-a651afff4282",
                "upperpc_code" : "UP1001",
                "user_code" : "Y001",
                "equip_code" : "XES-J21-PESM01",
                "process_code" : "C60-1",
                "wip_no" : "C1",
                "param_list" : [
                    {
                        "param_code" : "PM001",
                        "param_value" : "5.54"
                    },
                    {
                        "param_code" : "PM002",
                        "param_value" : "0.98"
                    }
                ],
                "time" : "2024-09-30 12:15:30",
                "data_source": "033"
            }]
    :return:
    """
    collection = get_prod_result_param_collection()
    await collection.insert_many(params)
    get_logger().info(f"prod_result_param插入数据:{params}")


async def get_ng_name(ng_code: str):
    """
    在ng配置表查询ng-code对应的名称
    :param ng_code:
    :return:
    """
    collection = get_basic_config()
    query_filter = {"unqualified_product_code": ng_code}
    result = await collection.find_one(query_filter)
    if result:
        return result["unqualified_product_name"]
    else:
        return ""


async def insert_unqualified_records_backup(params: list):
    """
    向不合格品记录备份表写入备份数据
    :param params:
    :return:
    """
    collection = get_unqualified_records_backup()
    await collection.insert_many(params)


async def get_process_info():
    """
    构建工序编码与工序顺序的映射关系
    :return:
    """
    collection = get_routing_process_info()
    process_code_sequence_map = {}
    async for rec in collection.find({}):
        process_code = rec['工序编码']
        process_sequence = rec['工序号']
        process_code_sequence_map.update({process_code: int(process_sequence)})
    update_context('process_code_sequence_map', process_code_sequence_map)
    get_logger().info(f"process_code_sequence_map:{process_code_sequence_map}")


async def aggregate_unqualified_records(qr_code_list: list, process_code: str) -> Tuple[str, List]:
    """
    对传入的码值进行不合格品记录校验
    :param qr_code_list:
    :param process_code:
    :return:
    """
    qr_code_map = {rec: rec for rec in qr_code_list}
    collection = get_unqualified_collection()
    error_msg = ""
    # 聚合条件: 获取不合格品记录处理结果是 报废(2)、未处理(0)的码值，如果存在这些码值，需要返回提示信息;
    pipeline_1 = [
        {'$match': {
            'qr_code': {"$in": qr_code_list},
            'process_result_code': {'$in': ['0', '2']}
        }},
        {'$group': {
            "_id": "$qr_code",
            'qr_code_records': {"$push": '$process_result_code'}
        }}
    ]
    async for rec in collection.aggregate(pipeline_1):
        qr_code = rec["_id"]
        qr_code_process_result_code_list: list = rec['qr_code_records']
        if '2' in qr_code_process_result_code_list:
            error_msg += f"{qr_code}为报废品;"
            qr_code_map.pop(qr_code)
        elif '0' in qr_code_process_result_code_list:
            error_msg += f"{qr_code}为不合格未处理品;"
            qr_code_map.pop(qr_code)
        else:
            pass
    re_input_wip = []
    # 获取不合格品记录处理结果是返工(4)的码值，每个码值可能存在多条不合格品记录，查询结果按照工序编码对应的工序号进行排序，取第一条.
    # 判断第一条记录的返工工序是不是当前工序，如果不是，则校验不通过，该码值需要提示信息；
    # 如果校验通过，则该码值在本工序返工，即二次进站，所以需要跳过重复进站校验
    process_code_sequence_map = get_context('process_code_sequence_map')
    pipeline_2 = [
        {'$match': {
            'qr_code': {"$in": list(qr_code_map.keys())},
            'process_result_code': '4',
            'is_done': 0
        }},
        {'$group': {
            "_id": "$qr_code",
            'qr_code_records': {
                '$push': {
                    'process_code': '$process_code',
                    'return_process_code': '$return_process_code',
                    'process_result_code': '$process_result_code'
                }}
        }}
    ]
    async for rec in collection.aggregate(pipeline_2):
        qr_code = rec["_id"]
        qr_code_records = rec.get('qr_code_records', [])
        qr_code_records = sorted(qr_code_records,
                                 key=lambda x: process_code_sequence_map.get(x['return_process_code'], float('inf')))
        first_record = qr_code_records[0]
        return_process_code = first_record.get('return_process_code')
        if return_process_code != process_code:
            error_msg += f"{qr_code}返工错误，应该从{return_process_code}工序进站;"
        else:
            re_input_wip.append(qr_code)
        qr_code_map.pop(qr_code)

    # 获取不合格品记录处理结果是复投(5)的码值，这些码值之前已经在本工序有过进站记录，但是复投需要二次进站，
    # 整理出来这些码值用于在重复进站校验时，跳过这些码值
    filter_1 = {
        'qr_code': {"$in": list(qr_code_map.keys())},
        'process_result_code': '5',
        'is_done': 0,
        'process_code': process_code
    }
    async for rec in collection.find(filter_1):
        qr_code = rec['qr_code']
        re_input_wip.append(qr_code)

    return error_msg, list(set(re_input_wip))


async def update_unqualified_records(qr_code_list: list, process_code: str):
    """
    在进站后更新不合格品记录信息
    :param qr_code_list:
    :param process_code:
    :return:
    """
    collection = get_unqualified_collection()
    update_content = {'is_done': 1}
    # 把所有不合格品登记表里的条码=进站条码、判定结果=返工、已完成is_done=0的都改成is_done=1
    filter_1 = {'qr_code': {"$in": qr_code_list},
                'process_result_code': '4',
                'is_done': 0}
    await collection.update_many(filter_1, {'$set': update_content})
    get_logger().info(f'下列查询条件{filter_1}下的不合格品记录完成更新{update_content}')
    # 把所有不合格品登记表里的条码=进站条码、判定结果=返工、登记工序=进站工序、已完成is_done=0的都改成is_done=1
    filter_2 = {'qr_code': {"$in": qr_code_list},
                'process_result_code': '5',
                'is_done': 0,
                'process_code': process_code}
    await collection.update_many(filter_2, {'$set': update_content})
    get_logger().info(f'下列查询条件{filter_2}下的不合格品记录完成更新{update_content}')


async def check_unqualified_record_027(qr_code_list: list) -> Tuple[str, List]:
    """
    校验027接口入参的电芯码是否存在不符合要求的不合格品记录: 只要这个条码的处理结果为未处理(0) 、或者报废(2)、或者部分报废(3)、
    或者返工(4)且is_done=0、或者复投(5)且is_done=0、或者降档(7)，都不允许组盘
    :param qr_code_list:
    :return:
    """
    qr_code_map = {rec: rec for rec in qr_code_list}
    collection = get_unqualified_collection()
    error_msg = ""
    # 校验以下场景：处理结果为未处理(0) 、或者报废(2)、或者部分报废(3)、降档(7)
    filter_1 = {
        'qr_code': {"$in": qr_code_list},
        'process_result_code': {'$in': ['0', '2', '3', '7']}
    }
    check_result_1 = []
    async for rec in collection.find(filter_1):
        check_result_1.append(rec['qr_code'])
        qr_code_map.pop(rec['qr_code'])
    if check_result_1:
        error_msg += f"以下条码{list(set(check_result_1))}存在处理结果为未处理(0) 、或者报废(2)、或者部分报废(3)、降档(7)的不合格品记录;"
    # 校验以下场景：返工(4)且is_done=0、或者复投(5)且is_done=0
    filter_2 = {
        'qr_code': {"$in": qr_code_list},
        'process_result_code': {'$in': ['4', '5']},
        'is_done': 0
    }
    check_result_2 = []
    async for rec in collection.find(filter_2):
        check_result_2.append(rec['qr_code'])
        qr_code_map.pop(rec['qr_code'])
    if check_result_2:
        error_msg += f"以下条码{list(set(check_result_2))}存在处理结果为返工(4)且is_done=0、或者复投(5)且is_done=0的不合格品记录;"

    return error_msg


async def check_wip_no_exists(code_list: list) -> Tuple[bool, dict, str]:
    """
    IF022/IF035接口，在入参工序是C100工序时，需要使用本方法将入参的wip_no(022)、bind_wip_no(035)转换为在本集合中关联的code
    :param code_list:
    :return:
    """
    """
    1、  IF022
    判断工序process_code是不是C100，如果是C100，需要修改成以下逻辑，对每一个进站码wip_no做一个转换。如果不是C100则按照原逻辑不变。
    从rule_code_record表中，查找wip_type=1 并且 bind_code=入参wip_no的第一条数据的code值（create_time倒排序），
    取到数据就把入参wip_no替换成这条数据的code来做后面的校验以及进站处理；取不到值就报错“进站码{wip_no}未绑定负极电芯码”
    2、  IF035
    判断工序process_code是不是C100，如果是C100，需要修改成以下逻辑，对每一个进站码bind_wip_no做一个转换。如果不是C100则按照原逻辑不变。
    从rule_code_record表中，查找wip_type=1 并且 bind_code=入参bind_wip_no的第一条数据的code值（create_time倒排序），
    取到数据就把入参bind_wip_no替换成这条数据的code来做后面的校验以及出站处理；取不到值就报错“进站码{in_cell_no}未绑定负极电芯码”
    """
    origin_code_new_code_map = {}
    collection = get_rule_code_collection()
    pipeline = [
        {'$match': {"wip_type": 1, "bind_code": {"$in": code_list}}},
        {"$sort": {"create_time": -1}},
        {"$group": {"_id": "$bind_code", "document": {"$first": "$$ROOT"}}}
    ]
    async for rec in collection.aggregate(pipeline):
        old_code = rec["_id"]
        document = rec["document"]
        new_code = document["code"]
        origin_code_new_code_map.update({old_code: new_code})
    new_code_list = list(origin_code_new_code_map.values())
    no_query_result_code = list(set(code_list) - set(new_code_list))
    if no_query_result_code:
        return False, origin_code_new_code_map, f"进站码{no_query_result_code}未绑定负极电芯码;"
    else:
        return True, origin_code_new_code_map, ""
